package apps.json;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

/**
 * Converts a simple formed Json String into a Java Object.
 * 
 * @author Rahul Jain <a mailto="">dynamicrahul2020 {at} gmail.com</a>
 * 
 */
public class SimpleJsonConverter {

    private static final String DELIM = ",";
    private static final String INTERIM_DELIM = ":";
    private static final String START_OBJECT = "{";
    private static final String END_OBJECT = "}";
    private static final String START_DATA = "\"";
    private static final String BLANK = "";
    private List<JsonElement> stack = new ArrayList<JsonElement>();

    /**
     * Converts a simple formed json string into a Java Object.
     * 
     * @param <T> type of class.
     * @param json json data that needs to be converted into java object.
     * @param clazz java class
     * @return
     * @throws JsonException If fails to set values or not able to find attribute name in class.
     */
    public <T> T jsonToJava(String json, Class<T> clazz) throws JsonException {
        readJson(json);
        return clazz.cast(getJavaObject(clazz));
    }

    private void readJson(String json) {
        json = json.replace(START_OBJECT, BLANK).replace(END_OBJECT, BLANK);
        StringTokenizer tokens = new StringTokenizer(json, DELIM);
        while (tokens.hasMoreTokens()) {
            String elem = tokens.nextToken();
            String[] arr = elem.split(INTERIM_DELIM);
            String key = arr[0].replace(START_DATA, BLANK);
            String value = arr[1].replace(START_DATA, BLANK);
            JsonElement element = new JsonElement(key, value);
            stack.add(element);
        }
    }

    private <T> Object getJavaObject(Class<T> clazz) throws JsonException {
        try {
            Object obj = getNewInstance(clazz);
            for (JsonElement element : stack) {
                Field f = clazz.getDeclaredField(element.getKey());
                f.setAccessible(true);
                setValue(f, obj, element.getValue());
            }
            return obj;
        } catch (InstantiationException ex) {
            throw new JsonException(ex);
        } catch (IllegalAccessException ex) {
            throw new JsonException(ex);
        } catch (ClassNotFoundException ex) {
            throw new JsonException(ex);
        } catch (SecurityException ex) {
            throw new JsonException(ex);
        } catch (NoSuchFieldException ex) {
            throw new JsonException(ex);
        }
    }

    private Object getNewInstance(Class clazz) throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return Class.forName(clazz.getName()).newInstance();
    }

    private static void setValue(Field f, Object obj, Object value) throws IllegalArgumentException,
            IllegalAccessException {
        f.set(obj, value);
    }
}
